﻿using System.Collections.Generic;
using System.Linq;

namespace KxParser.Parser.LR0
{
    public class ProductionItem
    {
        public string Name { get; set; }
        public List<string> Values = new List<string>();
        public int ConsumeLevel { get; set; }
        public State CurrentState { get; set; }
        public State NextState { get; set; }
        public string OverItem { get; set; }
        //public int? NextStateId { get; set; }

        public bool IsStarter
        {
            get
            {
                var startingProductionItem = (from pi in Global.ProductionItems
                                              where pi.Name.Equals(Global.Starter)
                                              select pi).FirstOrDefault();

                return Equals(startingProductionItem);
            }
        }

        public bool IsEnder {
            get
            {
                var startingProductionItem = (from pi in Global.ProductionItems
                                              where pi.Name.Equals(Global.Starter)
                                              select pi).FirstOrDefault();

                if (Values.Count != startingProductionItem.Values.Count)
                    return false;

                for(var i=0; i<Values.Count;i++)
                {
                    if(!startingProductionItem.Values[i].Equals(Values[i]))
                        return false;
                }

                if(ConsumeLevel == Values.Count)
                    return false;

                return OverItem.Equals(Global.Ender);

            }
        }

        private bool IsConsuleLevelOnEnd()
        {
            return ConsumeLevel == Values.Count;
        }

        public int ContainsRule
        {
            get
            {
                bool equal;

                for (var i = 0; i < Global.ProductionItems.Length;i++ )
                    {
                        equal = true;

                        //if (Values.Count != Global.ProductionItems[i].Values.Count)
                        if (!IsConsuleLevelOnEnd())
                            equal = false;

                        if (Values.Count != Global.ProductionItems[i].Values.Count)
                            equal = false;

                        if (!equal) continue;

                        for (var j = 0; j < Values.Count; j++)
                        {
                            if (Values[j] != Global.ProductionItems[i].Values[j])
                                equal = false;
                        }

                        if (equal)
                            return i;
                    }
                return -1;
            }
        }

        public ProductionItem(string productionItemString)
        {
            var idx1 = productionItemString.IndexOf(Global.Equalator);
            //var idx2 = productionItemString.IndexOf(Global.Ender);
            //if (idx2 == -1)
               var idx2 = productionItemString.Length;

            Name = productionItemString.Substring(0, idx1).Trim();
            var values = productionItemString.Substring(idx1 + 4, idx2 - idx1 - 4).Trim().Split(' ');
            Values.AddRange(values);
            ConsumeLevel = 0;
            NextState = null;
            OverItem = Global.Ender;
        }

        public ProductionItem(ProductionItem productionItem)
        {
            Name = productionItem.Name;
            Values.AddRange(productionItem.Values);
            ConsumeLevel = productionItem.ConsumeLevel;
            NextState = null;
            OverItem = Global.Ender;
        }

        public override string ToString()
        {
            var demarkatorPrinted = false;
            var str = Name + Global.Equalator;

            for (var i = 0; i < Values.Count;i++ )
            {
                if (i == ConsumeLevel)
                {
                    str += " " + Global.Demarkator;
                    demarkatorPrinted = true;
                }
                    str += " " + Values[i];
            }
            if(!demarkatorPrinted)
                str += " " + Global.Demarkator;

            str += string.Format(" [{1}]->{0} {2}{3}", ((NextState!=null) ? NextState.Id : -1), OverItem, (IsStarter)?"Starter":"", (IsEnder)?"Ender":"");
            return str;
        }

        /// <summary>
        /// Gets the non terminal right from demarkator.
        /// </summary>
        /// <returns>Non terminal right from demarkator. If value right from demarkator is not non terminal, NULL is returned</returns>
        public string GetRightValue()
        {
            return Values.Count > ConsumeLevel ? Values[ConsumeLevel] : null;
        }

        public bool IsNonTerminal(string value)
        {
            var q = Global.NonTerminals.FirstOrDefault(nonTerminal => nonTerminal.Equals(value)) != null;
            return q;
        }

        public bool CanConsume(string value)
        {
            var rightValue = GetRightValue();
            return (rightValue!=null && rightValue.Equals(value));
        }

        public ProductionItem Consume(string value)
        {
            if (!CanConsume(value))
                return null;

            var newProductionItem = new ProductionItem(this);
            newProductionItem.ConsumeLevel++;
            newProductionItem.NextState = null;

            return newProductionItem;
        }

        public override bool Equals(object obj)
        {
            if(typeof(ProductionItem).IsAssignableFrom(obj.GetType()))
            {
                var pi2 = (ProductionItem) obj;
                if(Name.Equals(pi2.Name) && ConsumeLevel.Equals(pi2.ConsumeLevel))
                {
                    if(Values.Count==pi2.Values.Count)
                    {
                        return !Values.Where((t, i) => !t.Equals(pi2.Values[i])).Any();
                    }
                    return false;

                }
                return false;
            }
            return false;
        }

        public int GetTerminalsCount()
        {
            return Values.Count(value => Global.Terminals.Contains(value));
        }


        
    }
}
